/*	Copyright 2007 - Xavier Baro (xbaro@cvc.uab.cat)

	This file is part of eapmlib.

    Eapmlib is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or any 
	later version.

    Eapmlib is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "SamplesEvaluator.h"

Evolutive::CSamplesEvaluator::CSamplesEvaluator(void) : CEvaluator(),m_ClassifierObj(NULL),m_SamplesSet(NULL),m_SamplesWeight(NULL),m_ErrorFunction(ERROR_MISSCLASS),m_SamplesValue(NULL),m_ThrMethod(THR_ZERO_FIXED),m_NormFac(NULL)
{	
#ifndef USE_OPENCV
	OPENCV_ERROR_CLASS("CSamplesEvaluator");
#endif
}

Evolutive::CSamplesEvaluator::~CSamplesEvaluator(void)
{	
	if(m_SamplesValue)
		delete[] m_SamplesValue;
	if(m_NormFac)
		delete[] m_NormFac;
}

#ifdef USE_OPENCV
void Evolutive::CSamplesEvaluator::SetSamplesSet(Evolutive::CSamplesSet *Samples)
{
	//! Store a pointer to the object
	m_SamplesSet=Samples;

	// Create the vector to store the classifier results
	if(m_SamplesValue)
		delete[] m_SamplesValue;
	m_SamplesValue=new double[m_SamplesSet->GetNumSamples()];

	// Create the vector for the normalization factor
	m_NormFac=new double[m_SamplesSet->GetNumSamples()];
}

void Evolutive::CSamplesEvaluator::SetClassifierObject(Evolutive::CClassifier *Classifier)
{
	//! Store a pointer to the object
	m_ClassifierObj=Classifier;
}

void Evolutive::CSamplesEvaluator::SetErrorFunction(Evolutive::ERROR_FUNCTION ErrFunc)
{
	//! Store the selected value
	m_ErrorFunction=ErrFunc;
}

void Evolutive::CSamplesEvaluator::SetSamplesWeight(double *Weights)
{
	//! Store a pointer to the samples weight distribution
	m_SamplesWeight=Weights;
}

double Evolutive::CSamplesEvaluator::GetScore(void)
{
	return GetScore(m_ClassifierObj);
}

double Evolutive::CSamplesEvaluator::GetScore(Evolutive::CChromosome *C)
{
	//! Initializes the classifier using the chromosome data
	m_ClassifierObj->SetFeatureParameters(C);

	return GetScore(m_ClassifierObj);
}

bool Evolutive::CSamplesEvaluator::IsValid(CChromosome *C)
{
	//! Initializes the classifier using the chromosome data
	m_ClassifierObj->SetFeatureParameters(C);

	return m_ClassifierObj->IsValid(C);
}

void Evolutive::CSamplesEvaluator::SetThrMethod(Evolutive::THR_METHOD Method)
{
	// Stores the new threshold method.
	m_ThrMethod=Method;
}

void Evolutive::CSamplesEvaluator::GetLastThreshold(double &Thr,double &Polarity)
{
	// Return the current values
	Thr=m_ThresholdValue;
	Polarity=m_Polarity;	
}

double Evolutive::CSamplesEvaluator::GetScore(Evolutive::CClassifier *C)
{
	register int i;
	double Error=1.0;
	CData *pData=NULL;

	// Apply the feature over all the samples
	for(i=0;i<m_SamplesSet->GetNumSamples();i++)
	{
		// Obtain the data structure (Memory leak)
		pData=m_SamplesSet->GetData(i);

		// Apply the feature
		m_SamplesValue[i]=C->Apply(pData);		

		// When we need normalization, store the normalization factor
		if(m_ThrMethod==THR_ADAPT_THR)
			m_NormFac[i]=m_SamplesSet->GetNormFactor(i);
		
		// Remove the data structure
		if(pData)
			delete pData;
	}

	// Set the threshold value
	switch(m_ThrMethod)
	{
	case THR_ZERO_FIXED:
		// Set a fixed value for the threshold and polarity
		m_ThresholdValue=0;
		m_Polarity=1;

		// Apply this threshold and polarity values
		Error=GetErrorValue(m_ThresholdValue,m_Polarity);
		break;
	case THR_ADAPT_THR:
		// Find the best threshold and polarity values
		Error=FindBestThreshold();
		break;
	}	
	
	return Error;
}

double Evolutive::CSamplesEvaluator::GetErrorValue(double Threshold, double Polarity)
{
	double Error;
	register int i;
	double E,ClassVal;

	// Initialize the error value
	Error=0;

	// Calculate the error value
	for(i=0;i<m_SamplesSet->GetNumSamples();i++)
	{
		// Obtain the class value using the classificator result
		ClassVal=((double)(m_SamplesValue[i]>=Threshold))*2.0-1.0;
		ClassVal*=Polarity;

		// Eval the error
		switch(m_ErrorFunction)
		{
		case ERROR_MISSCLASS:
			if(ClassVal!=m_SamplesSet->ExpectedVal(i))
			{
				Error++;
			}
			break;
		case ERROR_WEIGHTED:
			if(ClassVal!=m_SamplesSet->ExpectedVal(i))
			{
				Error+=m_SamplesWeight[i];
			}
			break;
		case ERROR_QUADRATIC:
			E=m_SamplesSet->ExpectedVal(i)-ClassVal;
			Error+=E*E;
			break;
		}
	}

	// Normalize the error
	switch(m_ErrorFunction)
	{
	case ERROR_MISSCLASS:
		Error/=m_SamplesSet->GetNumSamples();
		break;
	case ERROR_WEIGHTED:
		//! Normalization is not needed (the weights are normalized)		
		break;
	case ERROR_QUADRATIC:
		Error/=m_SamplesSet->GetNumSamples();
		break;
	}
	
	return Error;
}

double Evolutive::CSamplesEvaluator::FindBestThreshold(void)
{
	double *SortedVals=NULL;
	double Error=1.0;
	//double BestError=1.0;
	int NumSamples;
	register int i;
	
	//TODO: Find optimal threshold value

	// Store the number of samples
	NumSamples=m_SamplesSet->GetNumSamples();

	// Copy the scores to a new vector
	memcpy(SortedVals,m_SamplesValue,NumSamples*sizeof(double));

	// Sort the samples value from smaller to larger
	qsort(SortedVals,NumSamples,sizeof(double),DblCompareAsc);

	// Now calculate the error for each different threshold value
	for(i=0;i<NumSamples;i++)
	{

	}

	// Return the current values
	m_ThresholdValue=0;
	m_Polarity=1;
	
	return Error;
}
#endif //USE_OPENCV